Skip to content

Quote fewer characters, i.e. :, @, and + - #44

Merged
allenap merged 2 commits into
masterfrom
quote-fewer-characters
Aug 7, 2026
Merged

Quote fewer characters, i.e. :, @, and +#44
allenap merged 2 commits into
masterfrom
quote-fewer-characters

Conversation

@allenap

@allenap allenap commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Previously any one of :, @, or + dragged a whole word into quotes, so a URL came out as https'://github.com/allenap/shell-quote'. That is correct but noisy, which rather undermines the claim that Sh output is "better for humans to read, to copy and paste". All three are inert wherever they land in a word; each was checked in leading, medial, and trailing position against /bin/sh, Bash 3.2 and 5.3, Dash, Z Shell 5.9, and fish 4.8. The change is four lines of the classifier, with no position tracking and no new state, and every pre-existing test passes unmodified.

An earlier draft of this branch made % and = inert too, which would have addressed #8, but they needed a guard to re-quote them where they are significant, and that guard grew a new special case every time I looked at it: assignments, the append form FOO+=bar, = expansion in Z Shell, MAGIC_EQUAL_SUBST, and job specifications. Two of those I found only after convincing myself the implementation was already correct. Every one is conditional on a shell option, on job control, or on whether the shell is interactive, none of which a plain non-interactive sh -c probe exercises – which is precisely why they were missed. For a crate whose one job is to emit a single literal word, terser output isn't worth that, so % and = stay quoted and #8 stays open.

There is a module-level note in src/ascii.rs recording every hazard found, along with the lookalikes that turn out to be harmless, so that a future attempt at this starts from there rather than from scratch.

The second commit documents something related. A leading % cannot be made safe as a command name in Bash by any means, because Bash tests the word's value after quote removal, so %1, '%1', and $'%1' alike run fg. It is not conditional on job control; that only changes the complaint. Z Shell tests the literal token instead, so quoting does work there – which is why % is quoted unconditionally rather than only when it leads a word.

Closes #42.

Previously any one of these dragged a whole word into quotes, so a URL came out
as `https'://github.com/allenap/shell-quote'`. That is correct but noisy, which
rather undermines the claim that `Sh` output is "better for humans to read, to
copy and paste".

All three are inert wherever they land in a word, checked in leading, medial,
and trailing position against `/bin/sh`, Bash 3.2 and 5.3, Dash, Z Shell 5.9,
and fish 4.8.

`%` and `=` were tried too, and would have addressed #8 as well, but they
earned a guard that grew a new special case each time I looked at it – append
assignments, `=` expansion, `MAGIC_EQUAL_SUBST`, job specifications – and two
of those were found only after I had convinced myself the thing was correct.
For a crate whose one job is to emit a single literal word, terser output is
not worth that. They stay quoted, and the module documentation in `ascii.rs`
now records every hazard found, and the reason a plain non-interactive `sh -c`
probe fails to reveal them, so that the next person to try this starts where I
left off rather than where I began.

Closes #42.
@allenap
allenap force-pushed the quote-fewer-characters branch from 7825d59 to 717ab37 Compare August 7, 2026 21:43
@allenap allenap changed the title Quote fewer characters, i.e. :, @, +, %, and usually = Quote fewer characters, i.e. :, @, and + Aug 7, 2026
In command position Bash reads a word beginning with `%` as a job
specification, so `%1` on its own means `fg %1`, and quoting does not prevent
it: Bash tests the word's value, after expansion and quote removal, so `%1`,
`'%1'`, `"%1"`, `$'%1'`, `\%1`, and `""%1` all alike run `fg`.

Nor does it depend on job control being enabled, which was my first guess. Bash
rewrites the word either way and only the complaint differs, `fg: no job
control` or `fg: %1: no such job`; the same holds with `set -m`, with `set +m`,
under `--posix`, and in Bash 3.2. An executable named `%1` sitting on `PATH`
will not be run, though it runs happily by an explicit path. Only the first
character counts: `a%1` is an ordinary command name.

Z Shell tests the literal token instead, so quoting does work there. That is
why `%` is quoted unconditionally rather than only when it leads a word: it is
what Z Shell needs, and it is at worst harmless in Bash.

Worth keeping in proportion: this bites only in command position. As an
argument, which is what this crate is mostly used for, `%` is an ordinary
character in both shells.
@allenap
allenap force-pushed the quote-fewer-characters branch from 717ab37 to 5bfa942 Compare August 7, 2026 21:57
@allenap
allenap merged commit 0c736f0 into master Aug 7, 2026
3 checks passed
@allenap
allenap deleted the quote-fewer-characters branch August 7, 2026 21:59
@fenollp

fenollp commented Aug 11, 2026

Copy link
Copy Markdown

Thanks for your work!

and #8 stays open.

Note that #8 is closed tho ;)

@allenap

allenap commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

and #8 stays open.

Note that #8 is closed tho ;)

Ah, yes! I had a change of heart 😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sh: unexpected quoting of schemes in URLs ANSI-C quotes applied to strings without ANSI-C escape sequences

2 participants